//
//  USB_cocoa.m
//  amBXCocoa
//
//  Created by João Pedro Estilita Antunes on 23/11/09.
//  Copyright 2009 João Estilita. All rights reserved.
//  j.estilita@gmail.com
//

#import "USB_cocoa.h"

char speaker_left= 0x0B;
char speaker_right = 0x1B;
char washer_left = 0x2B;	
char washer_center = 0x3B;
char washer_right = 0x4B;

@implementation USB_cocoa

@synthesize red_global, green_global, blue_global;


- (void)rumblePadSpeed:(int)rumbleLeftSpeed:(int)rumbleRightSpeed
{
	char data[5];
	data[0]=0xA1;
	data[1]=0x7B;
	data[2]=0x02;
	data[3]=rumbleLeftSpeed;
	data[4]=rumbleRightSpeed;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}

- (void)sendFanSpeed:(int)leftSpeed:(int)rightSpeed
{
	
	char data[4];
	data[0]=0xA1;
	data[1]=0x5B;
	data[2]=0x01;
	data[3]=leftSpeed;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	
	data[1]=0x6B;
	data[3]=rightSpeed;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	
}

- (void)sendTimedSequence:(char)timeHighByte:(char)timeLowByte
{
	
	char data[5];
	data[0]=0xE1;
	data[2]=0x72;
	data[3]=timeHighByte;
	data[4]=timeLowByte;
	
	data[1]=0x0B;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	data[1]=0x1B;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}


- (void)sendValuesToSelectedLight:(int)speakerLeft:(int)washerLeft:(int)washerCenter:(int)washerRight:(int)speakerRight
{
	char data[6];
	data[0]=0xA1;
	data[2]=0x03;
	data[3]=red_global;
	data[4]=green_global;
	data[5]=blue_global;
	
	if (speakerLeft) {
		data[1]=speaker_left;
		(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	}
	if (washerLeft) {
		data[1]=washer_left;
		(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	}
	if (washerCenter) {
		data[1]=washer_center;
		(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	}
	if (washerRight) {
		data[1]=washer_right;
		(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	}
	if (speakerRight) {
		data[1]=speaker_right;
		(*interface)->WritePipe(interface, 1, &data, sizeof(data));
	}
}

- (void)sendValuesToSpeakerLeft:(int)red:(int)green:(int)blue
{
	char data[6];
	data[0]=0xE1;
	data[1]=speaker_left;
	data[2]=0x03;
	data[3]=red;
	data[4]=green;
	data[5]=blue;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}

- (void)sendValuesToSpeakerRight:(int)red:(int)green:(int)blue
{
	char data[6];
	data[0]=0xE1;
	data[1]=speaker_right;
	data[2]=0x03;
	data[3]=red;
	data[4]=green;
	data[5]=blue;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}

- (void)sendValuesToWasherLeft:(int)red:(int)green:(int)blue
{
	char data[6];
	data[0]=0xE1;
	data[1]=washer_left;
	data[2]=0x03;
	data[3]=red;
	data[4]=green;
	data[5]=blue;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}

- (void)sendValuesToWasherCenter:(int)red:(int)green:(int)blue
{
	char data[6];
	data[0]=0xE1;
	data[1]=washer_center;
	data[2]=0x03;
	data[3]=red;
	data[4]=green;
	data[5]=blue;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}

- (void)sendValuesToWasherRight:(int)red:(int)green:(int)blue
{
	char data[6];
	data[0]=0xE1;
	data[1]=washer_right;
	data[2]=0x03;
	data[3]=red;
	data[4]=green;
	data[5]=blue;
	(*interface)->WritePipe(interface, 1, &data, sizeof(data));
}


-(int)UpdateRed
{
	return red_global;
}
-(int)UpdateGreen
{
	return green_global;
}
-(int)UpdateBlue
{
	return blue_global;
}


-(void)CloseDevice
{
	IOObjectRelease(usbDevice);
}

-(void)OpenDevice
{
	usbVendor = 1137;
	usbProduct = 2111;
	// Set up a signal handler so we can clean up when we're interrupted from the command line
    // Otherwise we stay in our run loop forever.
	//    oldHandler = signal(SIGINT, SignalHandler);
	//    if (oldHandler == SIG_ERR) {
	//        fprintf(stderr, "Could not establish new signal handler.");
	//	}
	
    //fprintf(stderr, "Looking for devices matching vendor ID=%ld and product ID=%ld.\n", usbVendor, usbProduct);
	
    // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
    // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
    // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X" 
	// <http://developer.apple.com/qa/qa2001/qa1076.html>.
    // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching 
    // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will 
    // consume this dictionary reference, so there is no need to release it later on.
    
    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);	// Interested in instances of class
	// IOUSBDevice and its subclasses
    if (matchingDict == NULL) {
        fprintf(stderr, "IOServiceMatching returned NULL.\n");
        //return -1;
    }
    
    // We are interested in all USB devices (as opposed to USB interfaces).  The Common Class Specification
    // tells us that we need to specify the idVendor, idProduct, and bcdDevice fields, or, if we're not interested
    // in particular bcdDevices, just the idVendor and idProduct.  Note that if we were trying to match an 
    // IOUSBInterface, we would need to set more values in the matching dictionary (e.g. idVendor, idProduct, 
    // bInterfaceNumber and bConfigurationValue.
    
    // Create a CFNumber for the idVendor and set the value in the dictionary
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbVendor);
    CFDictionarySetValue(matchingDict, 
                         CFSTR(kUSBVendorID), 
                         numberRef);
    CFRelease(numberRef);
    
    // Create a CFNumber for the idProduct and set the value in the dictionary
    numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbProduct);
    CFDictionarySetValue(matchingDict, 
                         CFSTR(kUSBProductID), 
                         numberRef);
    CFRelease(numberRef);
    numberRef = NULL;
	
    // Create a notification port and add its run loop event source to our run loop
    // This is how async notifications get set up.
    
    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
    
    gRunLoop = CFRunLoopGetCurrent();
    CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);
    
    // Now set up a notification to be called when a device is first matched by I/O Kit.
    IOServiceAddMatchingNotification(gNotifyPort,					// notifyPort
									 kIOFirstMatchNotification,	// notificationType
									 matchingDict,					// matching
									 NULL,					// callback
									 NULL,							// refCon
									 &gAddedIter					// notification
									 );		
	
    // Iterate once to get already-present devices and arm the notification    
	//    DeviceAdded(NULL, gAddedIter);
	
	kern_return_t		kr;
	
    IOCFPlugInInterface	**plugInInterface;
    SInt32				score;
    HRESULT 			res;
	
	while ((usbDevice = IOIteratorNext(gAddedIter))) {
		
		io_name_t		deviceName;
		CFStringRef		deviceNameAsCFString;	
		UInt32			locationID;
		kr = IORegistryEntryGetName(usbDevice, deviceName);
		if (KERN_SUCCESS != kr) {
			deviceName[0] = '\0';
		}
		deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, 
														 kCFStringEncodingASCII);
		
		deviceName_private = deviceNameAsCFString;
		
		
		kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
											   &plugInInterface, &score);
		
		if ((kIOReturnSuccess != kr) || !plugInInterface) {
			fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
		}
		
		// Use the plugin interface to retrieve the device interface.
		res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
												 (LPVOID*) &deviceInterface_private);
		
		
		// Now done with the plugin interface.
		(*plugInInterface)->Release(plugInInterface);
		
		if (res || deviceInterface_private == NULL) {
			fprintf(stderr, "QueryInterface returned %d.\n", (int) res);
		}
		
		// Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
		// In this case, fetch the locationID. The locationID uniquely identifies the device
		// and will remain the same, even across reboots, so long as the bus topology doesn't change.
		
		kr = (*deviceInterface_private)->GetLocationID(deviceInterface_private, &locationID);
		if (KERN_SUCCESS != kr) {
			fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr);
		}
		else {
			//fprintf(stderr, "Location ID: 0x%lx\n\n", locationID);
		}
		
		locationID_private= locationID;
		
		
		kr = (*deviceInterface_private)->USBDeviceOpen(deviceInterface_private);    
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		IOUSBFindInterfaceRequest   request;
		io_iterator_t               iterator;
		IOCFPlugInInterface         **plugInInterface = NULL;
		
		HRESULT                     result;
		SInt32                      score;
		UInt8                       interfaceClass;
		UInt8                       interfaceSubClass;
		UInt8                       interfaceNumEndpoints;
		int                         pipeRef;
		
		//Placing the constant kIOUSBFindInterfaceDontCare into the following
		//fields of the IOUSBFindInterfaceRequest structure will allow you
		//to find all the interfaces
		request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
		request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
		request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
		request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
		
		//Get an iterator for the interfaces on the device
		kr = (*deviceInterface_private)->CreateInterfaceIterator(deviceInterface_private, &request, &iterator);
		while (usbDevice = IOIteratorNext(iterator))
		{
			//Create an intermediate plug-in
			kr = IOCreatePlugInInterfaceForService(usbDevice,
												   kIOUSBInterfaceUserClientTypeID,
												   kIOCFPlugInInterfaceID,
												   &plugInInterface, &score);
			//Release the usbDevice object after getting the plug-in
			kr = IOObjectRelease(usbDevice);
			if ((kr != kIOReturnSuccess) || !plugInInterface)
			{
				printf("Unable to create a plug-in (%08x)\n", kr);
				break;
			}
			
			//Now create the device interface for the interface
			result = (*plugInInterface)->QueryInterface(plugInInterface,
														CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
														(LPVOID *) &interface);
			//No longer need the intermediate plug-in
			(*plugInInterface)->Release(plugInInterface);
			
			if (result || !interface)
			{
				printf("Couldn’t create a device interface for the interface(%08x)\n", (int) result);
				break;
			}
			
			//Get interface class and subclass
			kr = (*interface)->GetInterfaceClass(interface,
												 &interfaceClass);
			kr = (*interface)->GetInterfaceSubClass(interface,
													&interfaceSubClass);
			
			//printf("Interface class %d, subclass %d\n", interfaceClass,interfaceSubClass);
			
			//Now open the interface. This will cause the pipes associated with
			//the endpoints in the interface descriptor to be instantiated
			kr = (*interface)->USBInterfaceOpen(interface);
			if (kr == kIOReturnExclusiveAccess) {
				printf("OPEN INTERFACE ERROR: kIOReturnExclusiveAccess\n");
			}else if (kr == kIOReturnError) {
				printf("OPEN INTERFACE ERROR: kIOReturnError\n");
			}
			if (kr != kIOReturnSuccess)
			{
				printf("Unable to open interface (%08x)\n", kr);
				(void) (*interface)->Release(interface);
				break;
			}
			
			//Get the number of endpoints associated with this interface
			kr = (*interface)->GetNumEndpoints(interface,
											   &interfaceNumEndpoints);
			if (kr != kIOReturnSuccess)
			{
				printf("Unable to get number of endpoints (%08x)\n", kr);
				(void) (*interface)->USBInterfaceClose(interface);
				(void) (*interface)->Release(interface);
				break;
			}
			
			//printf("Interface has %d endpoints\n", interfaceNumEndpoints);
			//Access each pipe in turn, starting with the pipe at index 1
			//The pipe at index 0 is the default control pipe and should be
			//accessed using (*usbDevice)->DeviceRequest() instead
			for (pipeRef = 1; pipeRef <= interfaceNumEndpoints; pipeRef++)
			{
				IOReturn        kr2;
				UInt8           direction;
				UInt8           number;
				UInt8           transferType;
				UInt16          maxPacketSize;
				UInt8           interval;
				char            *message;
				
				kr2 = (*interface)->GetPipeProperties(interface,
													  pipeRef, &direction,
													  &number, &transferType,
													  &maxPacketSize, &interval);
				if (kr2 != kIOReturnSuccess)
					printf("Unable to get properties of pipe %d (%08x)\n",
						   pipeRef, kr2);
				else
				{
					//printf("PipeRef %d: ", pipeRef);
					switch (direction)
					{
						case kUSBOut:
							message = "out";
							break;
						case kUSBIn:
							message = "in";
							break;
						case kUSBNone:
							message = "none";
							break;
						case kUSBAnyDirn:
							message = "any";
							break;
						default:
							message = "???";
					}
					//printf("direction %s, ", message);
					
					switch (transferType)
					{
						case kUSBControl:
							message = "control";
							break;
						case kUSBIsoc:
							message = "isoc";
							break;
						case kUSBBulk:
							message = "bulk";
							break;
						case kUSBInterrupt:
							message = "interrupt";
							break;
						case kUSBAnyType:
							message = "any";
							break;
						default:
							message = "???";
					}
					//printf("transfer type %s, maxPacketSize %d\n", message,maxPacketSize);
				}
			}
			
			
			//For this test, just use first interface, so exit loop
			break;
		}
		//		kr = IOObjectRelease(usbDevice);
		
	}
	
}

@end
